#include "znx_thread_mem.h"

#if (ZNX_THREAD_MEM_NO_POOL)

znx_thread_mem_freelists_t *
znx_thread_mem_freelists_create(size_t water_level)
{
    (void)water_level;
    znx_thread_mem_freelists_t *freelists;
    freelists = znx_mem_calloc(sizeof(znx_thread_mem_freelists_t));
    return freelists;
}


void *
znx_thread_mem_freelists_alloc(znx_thread_mem_freelists_t *freelists, size_t size)
{
    (void)freelists;
    return znx_mem_alloc(size);
}


void
znx_thread_mem_freelists_free(znx_thread_mem_freelists_t *freelists, void *ptr)
{
    (void)freelists;
    znx_mem_free(ptr);
}


void
znx_thread_mem_freelists_clean(znx_thread_mem_freelists_t *freelists)
{
    // nothing
}


void
znx_thread_mem_freelists_destroy(znx_thread_mem_freelists_t *freelists)
{
    znx_thread_mem_freelists_clean(freelists);
    znx_mem_free(freelists);
}


void
znx_thread_mem_freelists_status_report(
    znx_thread_mem_freelists_t *freelists, char *buf, size_t buf_size)
{
    snprintf(buf, buf_size, "no pool\n");
}


znx_thread_4kaligned_mem_freelists_t *
znx_thread_4kaligned_mem_freelists_create(size_t water_level)
{
    (void)water_level;
    znx_thread_4kaligned_mem_freelists_t *freelists;
    freelists = znx_mem_calloc(sizeof(znx_thread_4kaligned_mem_freelists_t));
    return freelists;
}


void *
znx_thread_4kaligned_mem_freelists_alloc(
    znx_thread_4kaligned_mem_freelists_t *freelists, size_t size)
{
    (void)freelists;
    return znx_4kaligned_mem_alloc(size);
}


void
znx_thread_4kaligned_mem_freelists_free(
    znx_thread_4kaligned_mem_freelists_t *freelists, void *ptr)
{
    (void)freelists;
    znx_4kaligned_mem_free(ptr);
}


void
znx_thread_4kaligned_mem_freelists_clean(znx_thread_4kaligned_mem_freelists_t *freelists)
{
    // nothing
}


void
znx_thread_4kaligned_mem_freelists_destroy(
    znx_thread_4kaligned_mem_freelists_t *freelists)
{
    znx_thread_4kaligned_mem_freelists_clean(freelists);
    znx_mem_free(freelists);
}


void
znx_thread_4kaligned_mem_freelists_status_report(
    znx_thread_4kaligned_mem_freelists_t *freelists,
    char *buf, size_t buf_size)
{
    snprintf(buf, buf_size, "no pool\n");
}


#else


znx_thread_mem_freelists_t *
znx_thread_mem_freelists_create(size_t water_level)
{
    int                         i;
    znx_thread_mem_freelists_t  *freelists;
    freelists = znx_mem_calloc(sizeof(znx_thread_mem_freelists_t));

    for (i = 0; i < ZNX_FREELIST_MODE_END; i++) {
        freelists->lists[i].space_size
            = znx_mem_freelist_mode_to_size(i);
        freelists->lists[i].water_level = water_level;
        freelists->lists[i].count = 0;
        freelists->lists[i].used = 0;
        freelists->lists[i].head = (void *)(-1);
    }

    freelists->inited = ZNX_TRUE;    

    return freelists;
}


void *
znx_thread_mem_freelists_alloc(znx_thread_mem_freelists_t *freelists, size_t size)
{
    if (size > ZNX_MEM_MAX_ALLOC_SIZE) {
        ZNX_ASSERT_FATAL("to big alloc size");
    }

    int                     i;
    int                     mode = -1;
    size_t                  mode_size;

    znx_mem_item_head_t     *item = NULL;
    znx_mem_freelist_t      *freelist = NULL;

    for (i = 0; i < ZNX_FREELIST_MODE_END; i++) {
        mode_size = (size_t)znx_mem_freelist_mode_to_size(i);
        if (size <= mode_size) {
            size = mode_size;
            mode = i;
            break;
        }
    }

    if (mode != -1 && freelists->inited) {
        freelist = &freelists->lists[mode];
    }

    if (freelist != NULL) {
        if (freelist->count > 0) {
            item = freelist->head;
            freelist->head = item->next;
            item->next = NULL;
            freelist->count--;
        }
        freelist->used++;
    }

    if (item == NULL) {
        return znx_mem_alloc(size);
    }

    return (void *)(item) + sizeof(znx_mem_item_head_t);
}


void
znx_thread_mem_freelists_free(znx_thread_mem_freelists_t *freelists, void *ptr)
{
    znx_mem_item_head_t     *item = NULL;
    znx_mem_freelist_t      *freelist = NULL;
    int                     mode = -1;
    int                     i;

    item = ptr - sizeof(znx_mem_item_head_t);
    assert(item->magic == ZNX_MEM_ITEM_MAGIC);
    if (item->next != NULL) {
        ZNX_ASSERT_FATAL("double free or mem stampede");
    }

    for (i = 0; i < ZNX_FREELIST_MODE_END; i++) {
        if (item->size == znx_mem_freelist_mode_to_size(i)) {
            mode = i;
            break;
        }
    }

    if (mode != -1 && freelists->inited) {
        freelist = &freelists->lists[mode];
    }

    if (freelist != NULL) {
        if (freelist->count < freelist->water_level) {
            item->next = freelist->head;
            freelist->head = item;
            item = NULL;
            freelist->count++;
        }
        freelist->used--;
    }

    if (item != NULL) {
        znx_mem_free(ptr);
    }
}


static void
znx_thread_mem_freelist_clean(znx_mem_freelist_t *freelist)
{
    znx_mem_item_head_t         *item;
    void                        *ptr;

    while (freelist->count > 0) {
        item = freelist->head;
        freelist->head = item->next;
        item->next = NULL;
        ptr = (void *)(item) + sizeof(znx_mem_item_head_t);
        znx_mem_free(ptr);
        freelist->count--;
    }
}


void
znx_thread_mem_freelists_clean(znx_thread_mem_freelists_t *freelists)
{
    int i;
    if (freelists->inited) {
        for (i = 0; i < ZNX_FREELIST_MODE_END; i++) {
            znx_thread_mem_freelist_clean(&freelists->lists[i]);
        }
    }
}


void
znx_thread_mem_freelists_destroy(znx_thread_mem_freelists_t *freelists)
{
    znx_thread_mem_freelists_clean(freelists);
    znx_mem_free(freelists);
}


void
znx_thread_mem_freelists_status_report(
    znx_thread_mem_freelists_t *freelists, char *buf, size_t buf_size)
{
    if (!freelists->inited) {
        snprintf(buf, buf_size, "no inited\n");
        return;
    }

    int                 off = 0;
    int                 i;
    size_t              size;
    size_t              count;
    size_t              used;
    znx_mem_freelist_t  *freelist = NULL;
    for (i = 0; i < ZNX_FREELIST_MODE_END; i++) {
        freelist = &freelists->lists[i];
        size = freelist->space_size;
        count = freelist->count;
        used = freelist->used;

        off += snprintf(buf + off, buf_size - (size_t)off,
            "%ld mode, count: %ld, used: %ld\n", size, count, used);
    }
}


znx_thread_4kaligned_mem_freelists_t *
znx_thread_4kaligned_mem_freelists_create(size_t water_level)
{
    znx_thread_4kaligned_mem_freelists_t *freelists;
    freelists = znx_mem_calloc(sizeof(znx_thread_4kaligned_mem_freelists_t));

    int                                 i;
    for (i = 0; i < MEM_4KALIGNED_FREELIST_MODE_END; i++) {
        freelists->lists[i].space_size
            = znx_4kaligned_mem_freelist_mode_to_size(i);
        freelists->lists[i].water_level = water_level;
        freelists->lists[i].count = 0;
        freelists->lists[i].used = 0;
        freelists->lists[i].head = (void *)(-1);
    }

    freelists->inited = ZNX_TRUE;

    return freelists;
}


void *
znx_thread_4kaligned_mem_freelists_alloc(
    znx_thread_4kaligned_mem_freelists_t *freelists, size_t size)
{
    if (size > ZNX_MEM_MAX_ALLOC_SIZE) {
        ZNX_ASSERT_FATAL("to big alloc size");
    }

    znx_mem_freelist_t          *freelist = NULL;
    znx_aligned_mem_item_head_t *item = NULL;
    int                         i;
    int                         mode = -1;
    size_t                      mode_size;

    for (i = 0; i < MEM_4KALIGNED_FREELIST_MODE_END; i++) {
        mode_size = (size_t)znx_4kaligned_mem_freelist_mode_to_size(i);
        if (size <= mode_size) {
            size = mode_size;
            mode = i;
            break;
        }
    }

    if (mode != -1 && freelists->inited) {
        freelist = &freelists->lists[mode];
    }

    if (freelist != NULL) {
        if (freelist->count > 0) {
            item = freelist->head;
            freelist->head = item->next;
            item->next = NULL;
            freelist->count--;
        }
        freelist->used++;   
    }

    if (item == NULL) {
        return znx_4kaligned_mem_alloc(size);
    }

    return ZNX_4KALIGNED_MEM_ITEM_PTR(item);
}


void
znx_thread_4kaligned_mem_freelists_free(
    znx_thread_4kaligned_mem_freelists_t *freelists, void *ptr)
{
    void                                *p1;
    znx_aligned_mem_item_head_t         *item;
    znx_mem_freelist_t                  *freelist = NULL;
    int                                 mode = -1;
    int                                 i;

    p1 = ((void **)ptr)[-1];
    item = (znx_aligned_mem_item_head_t *)p1;
    assert(item->magic == ZNX_ALIGNED_MEM_ITEM_MAGIC);
    if (item->next != NULL) {
        ZNX_ASSERT_FATAL("double free or mem stampede");
    }

    for (i = 0; i < MEM_4KALIGNED_FREELIST_MODE_END; i++) {
        if (item->size == znx_4kaligned_mem_freelist_mode_to_size(i)) {
            mode = i;
            break;
        }
    }

    if (mode != -1 && freelists->inited) {
        freelist = &freelists->lists[mode];
    }

    if (freelist != NULL) {
        if (freelist->count < freelist->water_level) {
            item->next = freelist->head;
            freelist->head = item;
            item = NULL;
            freelist->count++;
        }
        freelist->used--;
    }

    if (item != NULL) {
        znx_4kaligned_mem_free(ptr);
    }
}


static void
znx_thread_4kaligned_mem_freelist_clean(znx_mem_freelist_t *freelist)
{
    znx_aligned_mem_item_head_t *item;
    void                        *ptr;

    while (freelist->count > 0) {
        item = freelist->head;
        freelist->head = item->next;
        item->next = NULL;
        ptr = ZNX_4KALIGNED_MEM_ITEM_PTR(item);
        znx_4kaligned_mem_free(ptr);
        freelist->count--;
    }
}

void
znx_thread_4kaligned_mem_freelists_clean(znx_thread_4kaligned_mem_freelists_t *freelists)
{
    int i;

    if (freelists->inited) {
        for (i = 0; i < MEM_4KALIGNED_FREELIST_MODE_END; i++) {
            znx_thread_4kaligned_mem_freelist_clean(&freelists->lists[i]);
        }
    }
}


void
znx_thread_4kaligned_mem_freelists_destroy(
    znx_thread_4kaligned_mem_freelists_t *freelists)
{
    znx_thread_4kaligned_mem_freelists_clean(freelists);
    znx_mem_free(freelists);
}


void
znx_thread_4kaligned_mem_freelists_status_report(
    znx_thread_4kaligned_mem_freelists_t *freelists,
    char *buf, size_t buf_size)
{
    if (!freelists->inited) {
        snprintf(buf, buf_size, "no inited\n");
        return;
    }

    int                     off = 0;
    int                     i;
    size_t                  size;
    size_t                  count;
    size_t                  used;
    znx_mem_freelist_t      *freelist = NULL;
    for (i = 0; i < MEM_4KALIGNED_FREELIST_MODE_END; i++) {
        freelist = &freelists->lists[i];
        size = freelist->space_size;
        count = freelist->count;
        used = freelist->used;

        off += snprintf(buf + off, buf_size - (size_t)off,
            "%ld aligned mode, count: %ld, used: %ld\n", size, count, used);
    }
}


#endif
